/*
 * Created on 13.12.2003
 */
package ro.qi.framework.sys.watchers.filesystem;

import java.io.File;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * @author Denverash
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class FileWatcher {

	private static final long THREAD_TIMEOUT = 10000; 

	private static FileWatcher instance = new FileWatcher();
	private Hashtable<String, FileWatcherInfo> watches = new Hashtable<String, FileWatcherInfo>();
	private Object lock = new Object();
	private boolean running = false;
	private boolean started = false;

	public FileWatcher() {
	}

	public void start() {
		if( !started ) {
			started = true;
			new FileWatcherThread().start();
		}
	}

	public void stop() {
		started = false;
		running = false;
	}

	public void addWatch(FileWatcherListener listener, String filename) {
		// Logger.debug(this, "Adding listener for: " + filename);
		if( !watches.contains(filename) ) {
			FileWatcherInfo fileWatcherInfo = new FileWatcherInfo(filename);
			fileWatcherInfo.addListener(listener);
			watches.put(filename, fileWatcherInfo);
		} else {
			FileWatcherInfo fileWatcherInfo = (FileWatcherInfo)watches.get(filename);
			fileWatcherInfo.addListener(listener);
		}
	}

	public void deleteWatch(String filename) {
	}

	private class FileWatcherThread extends Thread {
		
		public FileWatcherThread() {
			super("FileWatcherThread");
		}

		public void run() {
			running = true;
			while( running ) {
				synchronized(lock) {
					Enumeration files = watches.keys();
					while( files.hasMoreElements() ) {
						String filename = (String)files.nextElement();
						FileWatcherInfo fileWatcherInfo = (FileWatcherInfo)watches.get(filename);
						fileWatcherInfo.check();
					}
				}
				try {
					sleep(THREAD_TIMEOUT);
				} catch (InterruptedException e) {}
			}
		}
	}

	private class FileWatcherInfo {
		public String filename;
		File file = null;
		public long lastCheck;
		private boolean exists;
		private boolean updating = false;
		Vector listeners = new Vector();

		public FileWatcherInfo(String _filename) {
			filename = _filename;
			file = new File(filename);
			if( !file.exists() ) {
				exists = false;
				lastCheck = 0;
			} else {
				exists = true;
				lastCheck = file.lastModified();
			}
		}

		public void check() {
			FileInfo fileInfo = new FileInfo();
			if( listeners == null )
				return;
			fileInfo.setExists(exists);
			fileInfo.setFilename(filename);
			fileInfo.setLastCheck(lastCheck);
			Enumeration enum1 = listeners.elements();
			if( file == null )
				return;
			if( updating ) {
				if( file.canWrite() ) {
					updating = false;
					lastCheck = new Date().getTime();
					if( exists ) {
						while(enum1.hasMoreElements())
							((FileWatcherListener)enum1.nextElement()).fileUpdated(fileInfo);
					} else {
						exists = true;
						while(enum1.hasMoreElements())
							((FileWatcherListener)enum1.nextElement()).fileAdded(fileInfo);
					}
				}
			} else if( !file.exists() && exists ) {
				exists = false;
				updating = false;
				while(enum1.hasMoreElements())
					((FileWatcherListener)enum1.nextElement()).fileDeleted(fileInfo);
			} else if( file.exists() && !exists ) {
				if( !file.canWrite() ) {
					updating = true;
				} else {
					exists = true;
					updating = false;
					lastCheck = new Date().getTime();
					while(enum1.hasMoreElements())
						((FileWatcherListener)enum1.nextElement()).fileAdded(fileInfo);
				}
			} else if( file.lastModified() > lastCheck ) {
				if( !file.canWrite() ) {
					updating = true;
				} else {
					updating = false;
					lastCheck = new Date().getTime();
					file = null;
					while(enum1.hasMoreElements())
						((FileWatcherListener)enum1.nextElement()).fileUpdated(fileInfo);
				}
			}
		}

		public void addListener(FileWatcherListener listener) {
			if( !listeners.contains(listener) )
				listeners.add(listener);
		}
		
		public void removeListener(FileWatcherListener listener) {
			if( listeners.contains(listener) )
				listeners.remove(listener);
		}
		
		public FileWatcherListener[] getListeners() {
			return (FileWatcherListener[])listeners.toArray(new FileWatcherListener[0]);
		}
	}

	public class Test implements FileWatcherListener {
		
		public Test() {
			FileWatcher.getInstance().addWatch(this, "d:\\b.txt");
			FileWatcher.getInstance().start();
		}
		
		public void run() {
			while(true){}
		}

		/**
		 * @see ro.qi.framework.watchers.filesystem.FileWatcherListener#fileAdded(ro.qi.framework.watchers.filesystem.FileInfo)
		 */
		public void fileAdded(FileInfo fileInfo) {
			System.out.println("File Added : " + fileInfo.getFilename());
		}

		/**
		 * @see ro.qi.framework.watchers.filesystem.FileWatcherListener#fileDeleted(ro.qi.framework.watchers.filesystem.FileInfo)
		 */
		public void fileDeleted(FileInfo fileInfo) {
			System.out.println("File Deleted : " + fileInfo.getFilename());
		}

		/**
		 * @see ro.qi.framework.watchers.filesystem.FileWatcherListener#fileUpdated(ro.qi.framework.watchers.filesystem.FileInfo)
		 */
		public void fileUpdated(FileInfo fileInfo) {
			System.out.println("File Updated : " + fileInfo.getFilename());
		}
	}

	public static FileWatcher getInstance() {
		return instance;
	}

	public static void main(String[] args) {
		Test test = new FileWatcher().new Test();
		test.run();
	}

}
